SPB Git

Repository Public MIT

page.tsx 3.0 KB 49 lines tsx

1import type { Metadata } from "next";2import { notFound } from "next/navigation";3import { ArtifactGrid, EmptyState } from "@/components/artifact-card";4import { TrustBadges } from "@/components/badges";5import { date, plural } from "@/lib/format";6import { publicCtx } from "@/lib/session";7import { getNamespace } from "@/server/auth";8import { listArtifacts } from "@/server/registry";910type P = { params: Promise<{ namespace: string }> };1112async function load(name: string) {13  const ctx = await publicCtx();14  const ns = await getNamespace(ctx, name);15  if (!ns) notFound();16  const [meta] = await ctx.sql<{ created_at: Date; kind: string; releases: number; last_release: Date | null }[]>`17    SELECT n.created_at, CASE WHEN n.owner_org_id IS NULL THEN 'Individual' ELSE 'Organization' END AS kind,18      (SELECT count(*)::int FROM releases r JOIN artifacts a ON a.id = r.artifact_id WHERE a.namespace_id = n.id AND r.state = 'published' AND a.visibility = 'public' AND a.state IN ('active','deprecated')) AS releases,19      (SELECT max(r.published_at) FROM releases r JOIN artifacts a ON a.id = r.artifact_id WHERE a.namespace_id = n.id AND r.state = 'published' AND a.visibility = 'public') AS last_release20    FROM namespaces n WHERE n.id = ${ns.id}`;21  const list = await listArtifacts(ctx, { namespace: ns.name, perPage: 100, sort: "updated" });22  return { ns, meta: meta!, list };23}24export async function generateMetadata({ params }: P): Promise<Metadata> {25  const { ns } = await load((await params).namespace);26  return { title: `@${ns.name}`, description: `Artifacts published by @${ns.name} on the Skills registry.` };27}28export default async function Page({ params }: P) {29  const { ns, meta, list } = await load((await params).namespace);30  return (31    <div className="container-page pb-10 pt-12">32      <header className="mb-10">33        <p className="eyebrow mb-3">Publisher · {meta.kind}</p>34        <h1 className="font-mono text-3xl font-medium tracking-tight">@{ns.name}</h1>35        {ns.display_name && <p className="mt-2 text-lg text-muted">{ns.display_name}</p>}36        <div className="mt-4 flex flex-wrap gap-1.5"><TrustBadges identityVerified={ns.identity_verified} sourceVerified={false} /></div>37        <dl className="mt-6 flex flex-wrap gap-x-10 gap-y-3 border-t border-line pt-5 text-sm">38          <div><dt className="text-muted">Public artifacts</dt><dd className="font-mono text-lg">{list.total}</dd></div>39          <div><dt className="text-muted">Published releases</dt><dd className="font-mono text-lg">{meta.releases}</dd></div>40          <div><dt className="text-muted">Last release</dt><dd className="font-mono text-lg">{date(meta.last_release)}</dd></div>41          <div><dt className="text-muted">Namespace created</dt><dd className="font-mono text-lg">{date(meta.created_at)}</dd></div>42        </dl>43      </header>44      <h2 className="mb-4 text-lg font-semibold tracking-tight">{plural(list.total, "published artifact")}</h2>45      {list.items.length ? <ArtifactGrid items={list.items} /> : <EmptyState title="This publisher has no public artifacts." />}46    </div>47  );48}49
Files main
Menu

Theme